#ifndef __SERVER_SIDE_H__
#define __SERVER_SIDE_H__

#include <Windows.h>
#include <winsock.h>
#include "common.h"

//macros for checking whether a given message header is file list request or file content request
#define IS_FILE_LIST_REQUEST(buf) (ntohs(MEM_TO_VAL((buf),unsigned short)) == PROTOCOL_PREFIX && MEM_TO_VAL((buf)+2,char) == DIR_CONTENT_REQ_MSG_ID)
#define IS_FILE_CONTENT_REQUEST(buf) (ntohs(MEM_TO_VAL((buf),unsigned short)) == PROTOCOL_PREFIX && MEM_TO_VAL((buf)+2,char) == FILE_CONTENT_REQ_MSG_ID)


/*
 * this message verify if a given UDP packet is indeed a "whois" message
 */
BOOL verifyClientHelloMsg(char* pBuffer,size_t suid)
{
	//extract protocol fields
	unsigned short protocolPrefix = ntohs(MEM_TO_VAL(pBuffer,unsigned short));
	char msgID = MEM_TO_VAL(pBuffer+2,char);
	size_t incommingSUid = ntohl(MEM_TO_VAL(pBuffer+3,size_t));

	//check if controll value match the calculations
	return (protocolPrefix == PROTOCOL_PREFIX && msgID == CLIENT_HELLO_MSG_ID && incommingSUid==suid);
}

/*
 * this function waits until udp packet arrives and return true if this is
 * client "whois" message
 */
BOOL waitForClientHello(SOCKET sd,size_t suid,struct sockaddr_in* pClient)
{
	fd_set readfds;
	char clientHelloMsg[CLIENT_HELLO_MSG_LEN];
	int recvAns,selectAns;
	int client_length = sizeof(struct sockaddr_in);

	do
	{
		printf("[+] waiting for client hellow message\n");
		FD_ZERO(&readfds);

#pragma warning(suppress:4127)
		FD_SET(sd,&readfds);
	   
		selectAns = select(sd+1,&readfds,NULL,NULL,NULL);

		//cehck if udp socket been hit
		if (selectAns == 0 || !FD_ISSET(sd,&readfds))
		{
			continue;
		}

		recvAns = recvfrom(sd, (char*)&clientHelloMsg, CLIENT_HELLO_MSG_LEN, 0, (sockaddr *)pClient, &client_length);

		//check if we got the whole data
		if (recvAns != CLIENT_HELLO_MSG_LEN)
		{
			continue;
		}
		
		//verify that this is the kind of message we need to responde to
		if (ntohs(pClient->sin_port) != CLIENT_UDP_PORT || !verifyClientHelloMsg((char*)&clientHelloMsg,suid))
		{
			continue;
		}
		printf("[+] got client hello message\n");
		return TRUE;

#pragma warning(suppress:4127) //this thread must live forever and serve ClientHello messages
	}while(TRUE);

	return FALSE;
}

/*
 * this function sends the server response for whois message
 */
int sendServerHello(SOCKET sd,unsigned short tcpPort,size_t suid,struct sockaddr_in* pClient)
{
	
	char serverHelloMsg[SERVER_HELLO_MSG_LEN];
	
	//set protocol values
	MEM_TO_VAL(serverHelloMsg,unsigned short) = htons(PROTOCOL_PREFIX);
	MEM_TO_VAL(serverHelloMsg+2,char) = SERVER_HELLO_MSG_ID;
	MEM_TO_VAL(serverHelloMsg+3,size_t) = htonl(suid);
	MEM_TO_VAL(serverHelloMsg+7,unsigned short) = htons(tcpPort);
	MEM_TO_VAL(serverHelloMsg+9,size_t) = htonl((suid^SERVER_UID_KEY) - 4*(tcpPort^SERVER_TCP_PORT_KEY));

	if (sendto(sd,(char*)serverHelloMsg,SERVER_HELLO_MSG_LEN,0,(sockaddr*)pClient,sizeof(struct sockaddr_in)) != SERVER_HELLO_MSG_LEN)
	{
		printf("Error: unable to respond to client discover request\n");
		return FALSE;
	}

	return TRUE;
}

/*
 * this function sends file content via TCP socket
 */
BOOL sendFileContent(SOCKET sd,char* pFileName)
{
	FILE* fd = NULL;
	int fileSize = 0;
	char msgPrefix[FILE_RES_HEADER_SIZE];
	char fileBuffer[FILE_BLOCK_SIZE];
	int bytesReadFromFile;

	do
	{
		if (fopen_s(&fd,pFileName,"rb"))
		{
			printf("[-] unable to open requested file %s",pFileName);
			break;
		}
		
		if (fseek(fd,0,SEEK_END))
		{
			printf("[-] unable to calculate file size\n");
			break;
		}

		fileSize = ftell(fd);

		if (fileSize == -1L)
		{
			printf("[-] unable to calculate file size\n");
			break;
		}

		if (fseek(fd,0,SEEK_SET))
		{
			printf("[-] unable to send file\n");
			break;
		}

		//set message's header's values
		MEM_TO_VAL(msgPrefix,unsigned short) = htons(0xF00D);
		MEM_TO_VAL(msgPrefix+2,char) = 0x04;
		MEM_TO_VAL(msgPrefix+3,size_t) = htonl(fileSize);

		if (sendTcpBuffer(sd,msgPrefix,FILE_RES_HEADER_SIZE)!=FILE_RES_HEADER_SIZE)
		{
			printf("[-] unable to send data to client\n");
			break;
		}
		
		//send file block by block
		while (fileSize > 0)
		{
			bytesReadFromFile = fread_s((char*)fileBuffer,FILE_BLOCK_SIZE,sizeof(char),FILE_BLOCK_SIZE,fd);
			
			if (sendTcpBuffer(sd,fileBuffer,bytesReadFromFile) != bytesReadFromFile)
			{
				printf("[-] unable to send data to client\n");
				break;
			}

			fileSize-=bytesReadFromFile;
		}
#pragma warning(suppress:4127) //enable single exit point		
	}while(FALSE);

	if (fd != NULL);
	{
		fclose(fd);
	}

	return fileSize == 0;

}
#endif

